{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多重派发"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**多重派发**是这篇notebook要探索的Julia的一个关键特性。\n",
    "\n",
    "它有助于快速开发。它也让软件可拓展、可设计和更好把玩。\n",
    "\n",
    "它可能是并行计算取得重大突破的预兆。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 罗马数字\n",
    "2. 函数\n",
    "3. 并行计算"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 罗马数字（玩玩）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们来定义一个**新结构**表示罗马数字。为了方便写代码，我们将只考虑0到9。\n",
    "\n",
    "**练习**：拓展到更大的数字。（别忘了罗马数字是十进制计数系统！）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "struct Roman\n",
    "    n::Int\n",
    "end\n",
    "\n",
    "Base.show(io::IO, r::Roman) = print(io, 'ⅰ' + (r.n - 1) % 10 )  # 漂亮的显示；'ⅰ'是一个Unicode罗马数字"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以像这样创建一个这个类型的对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "typeof.([5 5.0 Roman(5) \"Five\" '5'  5//1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "想用罗马数字漂亮地展示的话："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = [7 1 2 5 8 9]\n",
    "Roman.(x)   # equivalent to map(Roman, x)  or  [Roman(w) for w in x]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要是能像普通数字那样给罗马数字做加法就好了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4) + Roman(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但Julia不知道该怎么做。我们可以通过`import`函数`+`并拓展它的定义来教Julia给罗马数字做加法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base: +, *\n",
    "\n",
    "+(a::Roman, b::Roman) = Roman(a.n + b.n)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(4) + Roman(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这样就给函数`+`**添加了一个新方法**："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "methods(+)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base.*\n",
    "*(i::Roman, j::Roman) = Roman(i.n * j.n)                     # Multiply like a Roman"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * Roman(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman.(1:3) .* [Roman(1) Roman(2) Roman(3)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# mytimes基于类型来判断该做什么，很复杂\n",
    "# 不要纠结，更好的方法马上就来\n",
    "function mytimes(i,j)\n",
    "  if isa(i,Roman) & isa(j,Number)\n",
    "        return  fill(1, i.n, j)   # i by j matrix with ones\n",
    "    elseif    isa(i,Number) & isa(j,Roman) \n",
    "        return \"😄\"^ (i*j.n)   #  i * j happy faces\n",
    "    else\n",
    "        return(\"I Don't know\")\n",
    "    end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mytimes(4,Roman(3)) # 12个笑脸"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mytimes(Roman(4),3) # 4x3的全1矩阵"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最简单的实现方式是明确定义一个`Roman`和一个数字的乘法。我们可以按我们的想法来定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "*(i::Number, j::Roman) = \"😄\"^ (i*j.n)        #  i * j个笑脸\n",
    "\n",
    "*(i::Roman, j::Number) =   fill(1, i.n, j)       # i * j的矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "3 * Roman(3) # 9个笑脸"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "Roman(3) * 5  # 3*5的全1矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t(x::Roman,y::Roman) = x.n * y.n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "t(Roman(5),Roman(4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 注意它的汇编代码是多紧凑！\n",
    "@code_native t(Roman(2),Roman(4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import Base: *, +, ^"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "*(α::Number,   g::Function) = x -> α * g(x)   # 标量乘以函数\n",
    "\n",
    "*(f::Function, λ::Number)   = x -> f(λ * x)   # Scale the argument\n",
    "\n",
    "*(f::Function, g::Function) = x -> f(g(x))    # 复合函数  -- 滥用符号！  在Julia0.6中使用 \\circ\n",
    "\n",
    "^(f::Function, n::Integer) = n == 1 ? f : f*f^(n-1) # 一个通过递归乘法实现的天真的求幂算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "+(f::Function, g::Function) = x -> f(x) + g(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "举个例子，定义成这样的指数函数\n",
    "\n",
    "$$\\exp(x) = \\sum_{n=0}^\\infty \\frac{1}{n!} x^n.$$\n",
    "\n",
    "我们可以把它看成是这样的函数：\n",
    "\n",
    "$$\\exp = \\sum_{n=0}^\\infty \\frac{1}{n!} \\mathrm{pow}_n,$$\n",
    "\n",
    "其中$\\mathrm{pow}_n(x) = x^n$.\n",
    "\n",
    "（开始用数字模糊符号！）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pow(n) = x -> x^n\n",
    "\n",
    "myexp = sum(1/factorial(big(n)) * pow(n) for n in 0:100)   # 效率低的泰勒级数！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "[myexp(1); exp(1); exp(big(1))]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f = x -> x^2\n",
    "f(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = 3f\n",
    "g(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "(f^2)(10)  # 因为我们已经定义了函数乘法为符合函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "using Plots;\n",
    "gr()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = pi*(0:0.001:4)\n",
    "\n",
    "plot(x, sin.(x),    c=\"black\", label=\"Fun\")\n",
    "plot!(x, (12*sin).(x),    c=\"green\", label=\"Num * Fun\")\n",
    "plot!(x, (sin*12).(x),    c=\"red\", alpha=0.9, label=\"Fun * Num\")\n",
    "plot!(x, (5*sin*exp).(x), c=\"blue\", alpha=0.2, label=\"Num * Fun * Fun\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot([12*sin, sin*12, 5*sin*exp], 0:.01:4π, α=[1 .9 .2], c=[:green :red :blue])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"https://lh4.googleusercontent.com/--z5eKJbB7sg/UffjL1iAd4I/AAAAAAAABOc/S_wDVyDOBfQ/gauss.jpg\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  “我很讨厌sin^2 phi，虽然Laplace用过它；应该要担心sin^2 phi可能会引起歧义，如果说sin(phi^2)就不会或者说几乎不会引起歧义了，那么我们应该写(sin phi)^2，而不是sin^2 phi，以此类推sin^2 phi应该指的是sin(sin phi)。” -- Gauss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x=(0:.01:2) * pi;\n",
    "\n",
    "plot(x, (sin^2).(x), c=\"blue\")     # 乘方能工作，y=sin(sin(x))，Gauss会开心的！\n",
    "plot!(x, sin.(x).^2,  c=\"red\")         "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "h(a, b::Any) = \"fallback\"\n",
    "h(a::Number, b::Number) = \"a and b are both numbers\"\n",
    "h(a::Number, b) = \"a is a number\"\n",
    "h(a, b::Number) = \"b is a number\"\n",
    "h(a::Integer, b::Integer) = \"a and b are both integers\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 试着把玩h"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 1.0.5",
   "language": "julia",
   "name": "julia-1.0"
  },
  "language": "Julia",
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.0.5"
  },
  "toc": {
   "nav_menu": {
    "height": "119px",
    "width": "251px"
   },
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": "2",
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
